import { ComponentDatatype } from '../../Source/Cesium.js';
import { BufferUsage } from '../../Source/Cesium.js';
import { VertexArrayFacade } from '../../Source/Cesium.js';
import createContext from '../createContext.js';

describe('Renderer/VertexArrayFacade', function() {

    var context;

    beforeAll(function() {
        context = createContext();
    });

    afterAll(function() {
        context.destroyForSpecs();
    });

    it('creates a vertex array with static floats', function() {
        var positionIndex = 0;
        var vaf = new VertexArrayFacade(context, [{
            index : positionIndex,
            componentsPerAttribute : 3,
            componentDatatype : ComponentDatatype.FLOAT,
            usage : BufferUsage.STATIC_DRAW
        }], 1);

        var writer = vaf.writers[positionIndex];
        expect(writer).toBeDefined();

        writer(0, 1.0, 2.0, 3.0); // Write [1.0, 2.0, 3.0] at index zero.
        vaf.commit(); // Commit writes

        expect(vaf.va[0].va.getAttribute(0).vertexBuffer).toBeDefined();
        expect(vaf.va[0].va.getAttribute(0).vertexBuffer.sizeInBytes).toEqual(1 * 3 * 4);
        expect(vaf.va[0].va.getAttribute(0).vertexBuffer.usage).toEqual(BufferUsage.STATIC_DRAW);
        expect(vaf.va[0].va.getAttribute(0).componentsPerAttribute).toEqual(3);
        expect(vaf.va[0].va.getAttribute(0).componentDatatype).toEqual(ComponentDatatype.FLOAT);
        expect(vaf.va[0].va.getAttribute(0).offsetInBytes).toEqual(0);
        expect(vaf.va[0].va.getAttribute(0).strideInBytes).toEqual(3 * 4);
    });

    it('resizes a vertex array with static floats', function() {
        var positionIndex = 0;
        var vaf = new VertexArrayFacade(context, [{
            index : positionIndex,
            componentsPerAttribute : 3,
            componentDatatype : ComponentDatatype.FLOAT,
            usage : BufferUsage.STATIC_DRAW
        }], 1);

        var writer = vaf.writers[positionIndex];
        expect(writer).toBeDefined();

        writer(0, 1.0, 2.0, 3.0); // Write [1.0, 2.0, 3.0] at index zero.

        vaf.resize(2); // Two vertices
        writer(1, 1.0, 2.0, 3.0); // Write [4.0, 5.0, 6.0] at index one.
        vaf.commit(); // Commit writes

        expect(vaf.va[0].va.getAttribute(0).vertexBuffer).toBeDefined();
        expect(vaf.va[0].va.getAttribute(0).vertexBuffer.sizeInBytes).toEqual(2 * 3 * 4);
        expect(vaf.va[0].va.getAttribute(0).vertexBuffer.usage).toEqual(BufferUsage.STATIC_DRAW);
        expect(vaf.va[0].va.getAttribute(0).componentsPerAttribute).toEqual(3);
        expect(vaf.va[0].va.getAttribute(0).componentDatatype).toEqual(ComponentDatatype.FLOAT);
        expect(vaf.va[0].va.getAttribute(0).offsetInBytes).toEqual(0);
        expect(vaf.va[0].va.getAttribute(0).strideInBytes).toEqual(3 * 4);
    });

    it('creates a vertex array with static floats and unsigned bytes', function() {
        var positionIndex = 0;
        var colorIndex = 2;
        var vaf = new VertexArrayFacade(context, [{
            index : positionIndex,
            componentsPerAttribute : 3,
            componentDatatype : ComponentDatatype.FLOAT,
            usage : BufferUsage.STATIC_DRAW
        }, {
            index : colorIndex,
            componentsPerAttribute : 4,
            componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
            usage : BufferUsage.STATIC_DRAW
        }], 1);

        var positionWriter = vaf.writers[positionIndex];
        var colorWriter = vaf.writers[colorIndex];

        expect(positionWriter).toBeDefined();
        expect(colorWriter).toBeDefined();

        positionWriter(0, 1.0, 2.0, 3.0); // Write position [1.0, 2.0, 3.0] at index zero.
        colorWriter(0, 0, 255, 0, 255); // Write color [0, 255, 0, 255] at index zero.
        vaf.commit(); // Commit writes

        // Position attribute
        expect(vaf.va[0].va.getAttribute(0).vertexBuffer).toBeDefined();
        expect(vaf.va[0].va.getAttribute(0).vertexBuffer.sizeInBytes).toEqual(1 * ((3 * 4) + (4 * 1)));
        expect(vaf.va[0].va.getAttribute(0).vertexBuffer.usage).toEqual(BufferUsage.STATIC_DRAW);
        expect(vaf.va[0].va.getAttribute(0).componentsPerAttribute).toEqual(3);
        expect(vaf.va[0].va.getAttribute(0).componentDatatype).toEqual(ComponentDatatype.FLOAT);
        expect(vaf.va[0].va.getAttribute(0).offsetInBytes).toEqual(0);
        expect(vaf.va[0].va.getAttribute(0).strideInBytes).toEqual((3 * 4) + (4 * 1));

        // Color attribute
        expect(vaf.va[0].va.getAttribute(1).vertexBuffer).toEqual(vaf.va[0].va.getAttribute(0).vertexBuffer);
        expect(vaf.va[0].va.getAttribute(1).componentsPerAttribute).toEqual(4);
        expect(vaf.va[0].va.getAttribute(1).componentDatatype).toEqual(ComponentDatatype.UNSIGNED_BYTE);
        expect(vaf.va[0].va.getAttribute(1).offsetInBytes).toEqual(3 * 4);
        expect(vaf.va[0].va.getAttribute(1).strideInBytes).toEqual(vaf.va[0].va.getAttribute(0).strideInBytes);
    });

    it('creates a vertex array with static and dynamic attributes', function() {
        var positionIndex = 0;
        var txCoordIndex = 2;
        var vaf = new VertexArrayFacade(context, [{
            index : positionIndex,
            componentsPerAttribute : 3,
            componentDatatype : ComponentDatatype.FLOAT,
            usage : BufferUsage.STATIC_DRAW
        }, {
            index : txCoordIndex,
            componentsPerAttribute : 2,
            componentDatatype : ComponentDatatype.UNSIGNED_SHORT,
            usage : BufferUsage.DYNAMIC_DRAW,
            normalize : true
        }], 1);

        var positionWriter = vaf.writers[positionIndex];
        var txCoordWriter = vaf.writers[txCoordIndex];

        expect(positionWriter).toBeDefined();
        expect(txCoordWriter).toBeDefined();

        positionWriter(0, 1.0, 2.0, 3.0);
        txCoordWriter(0, 32 * 1024, 64 * 1024);
        vaf.commit();

        // Position attribute
        expect(vaf.va[0].va.getAttribute(0).vertexBuffer).toBeDefined();
        expect(vaf.va[0].va.getAttribute(0).vertexBuffer.sizeInBytes).toEqual(1 * (3 * 4));
        expect(vaf.va[0].va.getAttribute(0).vertexBuffer.usage).toEqual(BufferUsage.STATIC_DRAW);
        expect(vaf.va[0].va.getAttribute(0).componentsPerAttribute).toEqual(3);
        expect(vaf.va[0].va.getAttribute(0).componentDatatype).toEqual(ComponentDatatype.FLOAT);
        expect(vaf.va[0].va.getAttribute(0).offsetInBytes).toEqual(0);
        expect(vaf.va[0].va.getAttribute(0).strideInBytes).toEqual(3 * 4);

        // Texture coordinate attribute
        expect(vaf.va[0].va.getAttribute(1).vertexBuffer).toBeDefined();
        expect(vaf.va[0].va.getAttribute(1).vertexBuffer.sizeInBytes).toEqual(1 * (2 * 2));
        expect(vaf.va[0].va.getAttribute(1).vertexBuffer.usage).toEqual(BufferUsage.DYNAMIC_DRAW);
        expect(vaf.va[0].va.getAttribute(1).componentsPerAttribute).toEqual(2);
        expect(vaf.va[0].va.getAttribute(1).componentDatatype).toEqual(ComponentDatatype.UNSIGNED_SHORT);
        expect(vaf.va[0].va.getAttribute(1).offsetInBytes).toEqual(0);
        expect(vaf.va[0].va.getAttribute(1).strideInBytes).toEqual(2 * 2);
    });

    it('sub-commits', function() {
        var positionIndex = 0;
        var temperatureIndex = 2;
        var vaf = new VertexArrayFacade(context, [{
            index : positionIndex,
            componentsPerAttribute : 3,
            componentDatatype : ComponentDatatype.FLOAT,
            usage : BufferUsage.STATIC_DRAW
        }, {
            index : temperatureIndex,
            componentsPerAttribute : 1,
            componentDatatype : ComponentDatatype.FLOAT,
            usage : BufferUsage.STREAM_DRAW
        }], 2);

        var positionWriter = vaf.writers[positionIndex];
        var temperatureWriter = vaf.writers[temperatureIndex];

        expect(positionWriter).toBeDefined();
        expect(temperatureWriter).toBeDefined();

        positionWriter(0, 1.0, 2.0, 3.0);
        temperatureWriter(0, 98.6);
        positionWriter(1, 7.0, 8.0, 9.0);
        temperatureWriter(1, 32.0);
        vaf.commit();

        // Rewrite all vertices
        positionWriter(0, 10.0, 20.0, 30.0);
        temperatureWriter(0, 37.0);
        positionWriter(1, 70.0, 80.0, 90.0);
        temperatureWriter(1, 0.0);
        vaf.commit();

        // Sub-commit to just one vertex
        temperatureWriter(1, 212.0);
        vaf.subCommit(1, 1);
        vaf.endSubCommits();

        // Position attribute
        expect(vaf.va[0].va.getAttribute(1).vertexBuffer).toBeDefined();
        expect(vaf.va[0].va.getAttribute(1).vertexBuffer.sizeInBytes).toEqual(2 * (3 * 4));
        expect(vaf.va[0].va.getAttribute(1).vertexBuffer.usage).toEqual(BufferUsage.STATIC_DRAW);
        expect(vaf.va[0].va.getAttribute(1).componentsPerAttribute).toEqual(3);
        expect(vaf.va[0].va.getAttribute(1).componentDatatype).toEqual(ComponentDatatype.FLOAT);
        expect(vaf.va[0].va.getAttribute(1).offsetInBytes).toEqual(0);
        expect(vaf.va[0].va.getAttribute(1).strideInBytes).toEqual(3 * 4);

        // Temperature attribute
        expect(vaf.va[0].va.getAttribute(0).vertexBuffer).toBeDefined();
        expect(vaf.va[0].va.getAttribute(0).vertexBuffer.sizeInBytes).toEqual(2 * 4);
        expect(vaf.va[0].va.getAttribute(0).vertexBuffer.usage).toEqual(BufferUsage.STREAM_DRAW);
        expect(vaf.va[0].va.getAttribute(0).componentsPerAttribute).toEqual(1);
        expect(vaf.va[0].va.getAttribute(0).componentDatatype).toEqual(ComponentDatatype.FLOAT);
        expect(vaf.va[0].va.getAttribute(0).offsetInBytes).toEqual(0);
        expect(vaf.va[0].va.getAttribute(0).strideInBytes).toEqual(1 * 4);
    });

    it('destroys previous vertex buffers when number of vertices grows', function() {
        var positionIndex = 0;
        var vaf = new VertexArrayFacade(context, [{
            index : positionIndex,
            componentsPerAttribute : 3,
            componentDatatype : ComponentDatatype.FLOAT,
            usage : BufferUsage.STATIC_DRAW
        }], 1);

        var writer = vaf.writers[positionIndex];
        expect(writer).toBeDefined();

        writer(0, 1.0, 2.0, 3.0); // Write [1.0, 2.0, 3.0] at index zero.
        vaf.commit(); // Commit writes

        // Grab the vertex buffer
        var vbBeforeResize = vaf.va[0].va.getAttribute(0).vertexBuffer;

        vaf.resize(2); // Two vertices
        writer(1, 1.0, 2.0, 3.0); // Write [4.0, 5.0, 6.0] at index one.
        vaf.commit(); // Commit writes

        expect(vbBeforeResize.isDestroyed()).toBe(true);
        expect(vaf.va[0].va.getAttribute(0).vertexBuffer).not.toBe(vbBeforeResize);
    });

    it('is not initially destroyed', function () {
        var positionIndex = 0;
        var vaf = new VertexArrayFacade(context, [{
            index : positionIndex,
            componentsPerAttribute : 3,
            componentDatatype : ComponentDatatype.FLOAT,
            usage : BufferUsage.STATIC_DRAW
        }], 1);
        expect(vaf.isDestroyed()).toBe(false);
    });

    it('throws when constructed without a context', function() {
        expect(function() {
            return new VertexArrayFacade(undefined, undefined, undefined);
        }).toThrowDeveloperError();
    });

    it('throws when constructed undefined attributes', function() {
        expect(function() {
            return new VertexArrayFacade(context, undefined, undefined);
        }).toThrowDeveloperError();
    });

    it('throws when constructed without attributes', function() {
        expect(function() {
            return new VertexArrayFacade(context, []);
        }).toThrowDeveloperError();
    });

    it('throws when constructed with attributes without componentsPerAttribute', function() {
        expect(function() {
            return new VertexArrayFacade(context, [{}]);
        }).toThrowDeveloperError();
    });

    it('throws when constructed with attributes with an invalid componentDatatype', function() {
        expect(function() {
            return new VertexArrayFacade(context, [{
                componentsPerAttribute : 1,
                componentDatatype : 'invalid component datatype'
            }]);
        }).toThrowDeveloperError();
    });

    it('throws when constructed with attributes with an invalid usage', function() {
        expect(function() {
            return new VertexArrayFacade(context, [{
                componentsPerAttribute : 1,
                usage : 'invalid component usage'
            }]);
        }).toThrowDeveloperError();
    });

    it('throws when constructed with attributes with duplicate indices', function() {
        expect(function() {
            return new VertexArrayFacade(context, [{
                index : 0,
                componentsPerAttribute : 1
            }, {
                index : 0,
                componentsPerAttribute : 1
            }]);
        }).toThrowDeveloperError();
    });

    it('subCommit throws when passed an invalid offsetInVertices', function() {
        var positionIndex = 0;
        var vaf = new VertexArrayFacade(context, [{
            index : positionIndex,
            componentsPerAttribute : 3,
            componentDatatype : ComponentDatatype.FLOAT,
            usage : BufferUsage.STATIC_DRAW
        }], 10);

        expect(function() {
            vaf.subCommit(-1, 1);
        }).toThrowDeveloperError();

        expect(function() {
            vaf.subCommit(10, 1);
        }).toThrowDeveloperError();

        expect(function() {
            vaf.subCommit(1, 10);
        }).toThrowDeveloperError();
    });
}, 'WebGL');
